# -*- coding:utf-8 -*-
"""
Created on Apr 17, 2011

@author: Lithuanian
"""
from algorithm import Heuristic
class avoidPockets(Heuristic):
    
    pockets = None
    penaltyPerRobot = 10
    
    '''
    Calculates the total penalty of robots located in empty pockets.
    The penalty calculated as the disstace from the pocket exit
    '''
    def evaluate(self, problem_state):
        #first call to the heuristic - initialsize "pockt space"
        if self.pockets is None:
            self.pockets = self.initializePocketSpace(problem_state.obstacle_locations)
            #if no pockets were found
            if self.pockets is None:
                self.pockets = {}
                
        #fill pocket with piles
        for pile in problem_state.dirt_locations:
            for pocket in self.pockets:
                if pocket.isIn(pile):
                    pocket.pileNum += 1
        
        #the penalty for robots entering empty pockets!
        robotsInPockets = 0
        for robot in problem_state.robots :
            for pocket in self.pockets :
                if pocket.isIn(robot):
                    #rorbot is in the pocket
                    if pocket.pileNum == 0:
                        #pocket has no piles in it
                        robotsInPockets+= 1
        
        return self.penaltyPerRobot*robotsInPockets
        
    '''
    initialize the set of pockets
    @param obstacles: set of obstacale locations
    @param pile: set of pile locations
    @param width: problem space width
    @param height: problem space height   
    '''
    def initializePocketSpace(self, obstacles):
        pocketSet = set()
        done = set()
        currentPocket = set()
        heads = set()
        
        for obstacle in obstacles:
            if obstacle in done:
                continue
            currentPocket = set([obstacle])
            neighbour = None
            flag = False
            
            #look for pocket head
            for i in xrange(-1,2):
                if flag:
                    break
                for j in xrange(-1,2):
                    suspect = (obstacle[0] + i, obstacle[1] + j)
                    if (suspect[0] != obstacle[0] or suspect[1] != obstacle[1]) and suspect in obstacles and not suspect in done:
                        if neighbour is None:
                                neighbour = suspect
                        else:
                            #found more than one neighbout - 
                            #not pocket head - continue
                            flag = True
                            break
            if flag:
                continue
            if neighbour is None:
                continue                
            currentPocket.add(neighbour)
#            print obstacle
#            for i in xrange(-1,2):
#                for j in xrange(-1,2):
#                    if (obstacle[0]+i,obstacle[1]+j) in obstacles:
#                        print 'X',
#                    else:
#                        print ' ',
#                print ''
            #obstacle is pocket head
            heads.add(obstacle)
            done.add(obstacle)
#            print(obstacle, neighbour)
            m = (neighbour[0] - obstacle[0], neighbour[1] - obstacle[1])
            foundTurn = False 
            prev = neighbour
            done.add(prev)
            neighboursCount = 1
            foundPocketEnd = False
            #search for the other side of the pocket
            while not foundPocketEnd:
                for i in xrange(-1,2):
                    for j in xrange(-1,2):
                        suspect = (prev[0] + i, prev[1] + j)
                        if suspect == prev:
                            continue
                        else:
                            if suspect in obstacles and not suspect in done:
                                if m != (i,j):
                                    foundTurn = True
                                newPrev = suspect
                                neighboursCount += 1
                                continue
                if neighboursCount == 1:
                    heads.add(prev)
                    foundPocketEnd = True
                else:
                    prev = newPrev
                    currentPocket.add(prev)
                    foundPocketEnd = False
                done.add(prev)
                neighboursCount = 1
            if foundTurn:
                rect = self.findCorners(currentPocket)                    
                pocketSet.add(Pocket(rect[0], rect[1]))
#                print(rect[0], rect[1])
#        print "--heads--"
#        print heads
#        print "-------"
#        print "--pockets--"
#        print map(lambda x: (x.lowerLeft, x.upperRight), pocketSet)
        return pocketSet
        
    
    def findCorners(self, pocket):
        up = None
        right = None
        left = None
        bottom = None
        
        for obstacle in pocket:
            if up is None:
                up = obstacle[1]
                right = obstacle[0]
                left = obstacle[0]
                bottom = obstacle[1]
            else:
                if up > obstacle[1]: 
                    up = obstacle[1]
                if bottom < obstacle[1]:
                    bottom = obstacle[1]
                if left > obstacle[0]:
                    left = obstacle[0]
                if right < obstacle[0]:
                    right = obstacle[0]
        return ((left, bottom), (right, up))
    
class Pocket:
    #Open side constants
    UP      =   0
    RIGHT   =   1
    DOWN    =   2
    LEFT    =   3
    
    '''
    @param p1..p4: the four corners of the pocket
    @param openSide: the open entry side of the pocket
    '''
    def __init__(self, lowerLeft, upperRight):
        self.piles      = 0
        self.lowerLeft  = lowerLeft
        self.upperRight = upperRight
    
    '''
    @param point: the poing we want to check
    @return: true iff the point is in the pocket 
    '''
    def isIn(self, point):
        if point[0] > self.lowerLeft[0] and point[0] < self.upperRight[0] :
            #point's x vlaue is in pocket range
            if point[1] > self.lowerLeft[1] and point[1] < self.upperRight[1] :
                #point's y value is in pocket range => the point is in the pocket
                return True
        return False
#    '''
#    @return: the disstance of a point from pocket exit 
#    '''
#    def distanceFromExit(self, point):
#        if point[0] > self.lowerLeft[0] and point[0] < self.upperRight[0] :
#            #point's x vlaue is in pocket range
#            if point[1] > self.lowerLeft[1] and point[1] < self.upperRight[1] :
#                #point's y value is in pocket range => the point is in the pocket
#                return self.calculateDistanceFromExit(point)
#        return -1
            
#    def calculateDistanceFromExit(self, point):
#        if self.openSide == self.UP :
#            return abs(point[1] - self.upperRight[1])
#        if self.openSide == self.DOWN:
#            return abs(point[1] - self.lowerLeft[1])
#        if self.openSide == self.LEFT:
#            return abs(point[0] - self.lowerLeft[0])
#        if self.openSide == self.RIGHT:
#            return abs(point[0] - self.upperRight[0])